Preprocesing data

Przekształcanie i czyszczenie danych


Ten dokument opisuje sposoby, możliwości i techniki porządkowania i czyszczenia danych z użyciem biblioteki (Pandas) i (Polars) python.

Mam nadzieję, że będzie to dla Ciebie przydatne!
Miłej lektury!
Author

Piotr Dłubak

Published

February 18, 2025

Wprowadzenie

Analiza danych:

  1. Badana populacja: Klienci sklepów spożywczych

  2. Rodzaj badania: Ankieta on-line dostępna dla wszystkich użytkowników na terenie Polski

  3. Metoda analizy: EDA (“Exploratory Data Analysis”) - analiza deskrypcyjna

  4. Parametry badania:

    Zmienne ilościowe:

    • wiek
    • liczba osób w rodzinie
    • dochody
    • wydatki

    Zmienne jakościowe:

    • płeć
    • wykształcenie
    • preferowany typ sklepu
    • preferowana marka sklepu
    • preferowanay towar
    • czynnik zakupowy
    • rodzaj promocji
    • miasto

Wyniki przeprowadzonej analizy pokazały, że dla współczesnego konsumenta decyzja o zakupie produktu nie jest motywowana wyłącznie chęcią zaspokojenia określonych potrzeb, ale w znacznym stopniu determinuje ją potrzeba demonstracji przekonań, statusu społeczno-ekonomicznego i stylu życia. Nowoczesny model konsumpcji niesie określone konsekwencje dla praktyki działań marketingowych.

Badanie ankietowe dotyczyło postaw i zachowań respondentów w trakcie dokonywania zakupów produktów żywnościowych. W tym celu został skonstruowany kwestionariusz ankiety.

Cechy, takie jak wiek, płeć, wykształcenie, preferowany typ sklepu, dochody netto, preferowana marka sklepu, preferowany towar, preferowany rodzaj promocji oraz czynnik zakupowy, mogą wpływać na wysokość zakupów żywnościowych.

Wymagania dotyczące danych: Zdefiniowenie zakresu analizy (zmienne, obserwacje, zakresy dziedzinowe)
  1. Zmienne ilościowe:
    • wiek: 13-105 lat
    • liczba osób w rodzinie: 1-20
    • dochody: >0
    • wydatki: >0
  2. Zmienne jakościowe:
    • płeć: “mężczyzna”, “kobieta”
    • wykształcenie: “podstawowe”, “zawodowe”, “średnie”, “wyższe”
    • preferowany typ sklepu: “bazarek”, “osiedlowy”, “supermarket”, “galeria”
    • preferowana marka sklepu: dowolne
    • preferowany towar: dowolne
    • czynnik zakupowy: dowolne
    • rodzaj promocji: “sugestia kasjera”, “gazetka”, “reklama RTV”, “sms”, “e-mail”, “karta”, “aplikacja”, “nie korzystam”
    • miasto: dowolne
Co to jest Preprocesing danych?

Preprocessing danych – co to jest i dlaczego jest ważny?

1. Czym jest preprocessing danych?

Preprocessing danych to zbiór technik i operacji stosowanych w celu przygotowania surowych danych do analizy, eksploracji czy modelowania. Obejmuje on czyszczenie, transformację i normalizację danych, aby poprawić ich jakość i ułatwić dalsze przetwarzanie.

2. Kluczowe etapy preprocessingu danych

  • Usuwanie brakujących wartości – wypełnianie braków metodami statystycznymi (np. średnia, mediana) lub usuwanie niekompletnych rekordów.
  • Obsługa wartości odstających – identyfikacja i eliminacja anomalii, które mogą zaburzać analizę.
  • Czyszczenie danych z błędów – poprawianie literówek, błędnych wpisów i formatowania danych.
  • Usuwanie duplikatów – eliminacja powtarzających się rekordów, które mogą fałszować wyniki analizy.
  • Wykrywanie i poprawa niezgodności danych – identyfikacja sprzecznych informacji (np. ujemny wiek, błędne daty) i ich korekta.
  • Standaryzacja i normalizacja – przekształcanie danych do jednolitej skali w celu poprawy wydajności modeli ML.
  • Kodowanie zmiennych kategorycznych – zamiana wartości tekstowych na wartości numeryczne (np. one-hot encoding, label encoding).
  • Usuwanie lub redukcja zmiennych – eliminacja kolinearnych lub nieistotnych cech, co zwiększa efektywność analizy.
  • Tworzenie cech (feature engineering) – generowanie nowych zmiennych, które lepiej reprezentują dane.
  • Redukcja wymiarowości – stosowanie technik takich jak PCA w celu zmniejszenia liczby zmiennych wejściowych.
  • Dekompozycja danych czasowych – wydzielanie sezonowości, trendów i anomalii w szeregach czasowych.

3. Dlaczego preprocessing danych jest ważny?

  • Poprawa jakości danych – usuwa błędy, brakujące wartości i anomalia, co zapobiega błędnym wnioskom.
  • Zwiększenie wydajności modeli ML – dobrze przygotowane dane poprawiają skuteczność algorytmów uczenia maszynowego.
  • Redukcja szumu – minimalizuje wpływ nieistotnych lub błędnych informacji.
  • Lepsza interpretacja wyników – przejrzyste i dobrze przygotowane dane ułatwiają analizę i wyciąganie wniosków.
  • Efektywność obliczeniowa – optymalizacja struktury danych zmniejsza czas i zasoby potrzebne do analizy.

Dobrze wykonany preprocessing jest kluczowym etapem analizy danych i ma bezpośredni wpływ na jakość oraz dokładność wyników! 🚀

“Co to jest analiza EDA?”
  • Analiza deskrypcyjna to proces badania, opisywania i interpretacji danych w celu uzyskania wglądu i zrozumienia ich cech, wzorców i związków. Jest to technika często stosowana w dziedzinach naukowych, badań społecznych, statystyce, lingwistyce i wielu innych dziedzinach. Głównym celem analizy deskrypcyjnej jest opisanie i podsumowanie danych w sposób, który ujawnia istotne informacje. Może obejmować takie elementy jak obliczanie średnich, median, odchyleń standardowych, minimalnych i maksymalnych wartości, oraz prezentowanie danych w postaci tabel, wykresów lub grafów.
  • Analiza deskrypcyjna umożliwia identyfikację kluczowych cech, trendów, anomalii i relacji w danych. Może również pomóc w odkrywaniu wzorców, porównywaniu grup lub kategorii danych oraz wyprowadzaniu wniosków na podstawie zebranych informacji. Głównym celem EDA jest zapewnienie wglądu w dane jeszcze przed sformułowaniem jakichkolwiek założeń. Pomaga identyfikować oczywiste błędy, lepiej pojmować wzorce występujące w obrębie danych, wykrywać wartości odstające i anomalie, a także odnajdywać interesujące relacje między zmiennymi. Po przeprowadzeniu analizy EDA i uzyskaniu istotnych spostrzeżeń wciąż można wykorzystać tę metodę do bardziej zaawansowanej analizy danych lub modelowania, w tym na potrzeby uczenia maszynowego.

flowchart LR
  A{Preprocesing danych} --> B(Pozyskanie danych)
  A --> C(Porządkowanie danych)
  A --> D(Czyszczenie danych)
  B --> E[Źródła danych]
  B --> F[Pobieranie danych]



  1. Źródła danych

Dane można pozyskiwać z różnych źródeł w zależności od celu analizy: a) Dane publiczne i otwarte

Bazy danych rządowe – np. Eurostat, GUS, UCI Machine Learning Repository, Open Data Portal.
Dane naukowe – Kaggle Datasets, Google Dataset Search.
APIs – np. Twitter API, OpenWeatherMap, Google Maps API.
  1. Wewnętrzne źródła danych

    Bazy relacyjne (SQL, NoSQL) – np. PostgreSQL, MongoDB. Dane logów i telemetryczne – np. pliki JSON, Parquet, Apache Kafka. Dane biznesowe – CRM, ERP, systemy księgowe.

  2. Dane generowane przez użytkowników

    Ankiety i formularze – Google Forms, SurveyMonkey. Social media – Facebook Graph API, YouTube API. Systemy IoT – sensory, urządzenia smart.]

  1. Pobieranie danych

Dane można pobierać na kilka sposobów: a) Pobieranie ręczne

Pobranie plików CSV, Excel, JSON, XML.
Eksport z baz danych.
  1. Web Scraping

    Biblioteki Python: BeautifulSoup, Scrapy, Selenium. API REST: requests, urllib.

  2. Automatyczna ekstrakcja i strumieniowanie danych

    Apache Kafka, Spark Streaming – do przetwarzania danych w czasie rzeczywistym. ETL (Extract, Transform, Load) – np. Airflow, Talend, dbt.

I. Wstępne przetwarzanie

1. Przygotowanie danych

1.1. Pozyskanie danych

  • Dane pozyskano z przeprowadzonego badania ankietowego, które dotyczyło postaw i zachowań respondentów w trakcie dokonywania zakupów produktów żywnościowych. W tym celu został skonstruowany kwestionariusz ankiety. Dane ankietowe zostały zebrane w formie plików, które następnie załadowano do analizy. Pliki te zawierają informacje o respondentach, takie jak wiek, płeć, wykształcenie, preferencje zakupowe oraz czynniki wpływające na decyzje zakupowe.

  • Szczegółowe dane zostały zapisane w następujących plikach:

    1. ankieta_01a.xlsx
    2. ankieta_01b.xlsx
    3. ankieta_02.csv
    4. miasta.json

1.2. Pobranie i załadowanie danych

Dane te zostały załadowane do odpowiednich DataFrame’ów:

Plik DataFrame Opis
ankieta_01a.xlsx ankieta_01a Zawiera podstawowe informacje o respondentach.
ankieta_01b.xlsx ankieta_01b Zawiera dodatkowe informacje o respondentach.
ankieta_02.csv ankieta_02 Zawiera dane dotyczące dochodów i wydatków respondentów.
miasta.json miasta Zawiera informacje o miastach, z których pochodzą respondenci.

1.3. Podgląd tabel

Code
import warnings
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import statsmodels.api as sm
import random
from scipy.stats import zscore
import re
import math
warnings.filterwarnings("ignore")
from IPython.display import display, Markdown
import polars as pl




ankieta_01a = pd.read_excel('ankieta_01a.xlsx')
ankieta_01b = pd.read_excel('ankieta_01b.xlsx')
ankieta_02 = pd.read_csv('ankieta_02.csv', skiprows=1, skipfooter=1, delimiter=';',engine='python')
miasta = pd.read_json('miasta.json')



def display_table_info(tables):
    info = []
    for name, df in tables.items():
        info.append({
            'Nazwa': name,
            'Liczba kolumn': df.shape[1],
            'Liczba wierszy': df.shape[0]
        })
    return pd.DataFrame(info)

tables = {
    'ankieta_01a': ankieta_01a,
    'ankieta_01b': ankieta_01b,
    'ankieta_02': ankieta_02,
    'miasta': miasta
}


def unique_values_summary(tables):
    summary = []
    for table_name, df in tables.items():
        for col in df.columns:
            unique_values = df[col].unique()
            summary.append({
                'Nazwa tabeli': table_name,
                'Nazwa kolumny': col,
                'Liczba unikatowych wartości': len(unique_values),
                'Lista unikatowych wartości': unique_values
            })
    return pd.DataFrame(summary)

unique_summary_df = unique_values_summary(tables)
display(unique_summary_df)





table_info = display_table_info(tables)
display(table_info)


def display_dataframe_with_title(df, title):
    display(Markdown(f"#### {title}"))
    display(df.head())

print()
#
display(Markdown("**WYŚWIETLENIE ZAWARTOŚCI TABEL - 5 pierwszych wierszy**"))
print()
display_dataframe_with_title(ankieta_01a, "Tabela: ankieta_01a")
display_dataframe_with_title(ankieta_01b, "Tabela: ankieta_01b")
display_dataframe_with_title(ankieta_02, "Tabela: ankieta_02")
display_dataframe_with_title(miasta, "Tabela: miasta")

Nazwa tabeli Nazwa kolumny Liczba unikatowych wartości Lista unikatowych wartości
0 ankieta_01a id. 59 [R_001, R_002, R_003, R_004, R_005, R_006, R_0...
1 ankieta_01a kod_miasta 39 [M_058, M_050, M_069, M_059, M_057, M_001, M_0...
2 ankieta_01a wiek|liczba osób w rodzinie 52 [30|2, 16|1, 49|1, 67 lat|1, 38|5, 28|1, 158|5...
3 ankieta_01a m_wykształcenie 7 [podstawowe, podstawowe., zawodowe, średn, śre...
4 ankieta_01a k_wykształcenie 5 [nan, podstawowe, zawodowe, śred5nie, średnie]
5 ankieta_01a PREFEROWANY TYP SKLEPU 4 [BAZAREK, OSIEDLOWY, SUPERMARKET, GALERIA]
6 ankieta_01a preferowana marka sklepu 13 [POLOMARKET, NETTO, BIEDRONKA, LIDL, DINO, KAU...
7 ankieta_01a preferowanay towar 7 [Napoje, Owoce i warzywa, Produkty piekarnicze...
8 ankieta_01a preferowany rodzaj promocji 8 [sugestia, gazetka, reklama, karta, sms, email...
9 ankieta_01a preferowany rodzaj promocji.1 4 [kasjera, nan, RTV, korzystam]
10 ankieta_01a czynnik zakupowy 6 [cena, dostępność, jakość, marka, skład, lokal...
11 ankieta_01b id. 66 [R_060, R_061, R_062, R_063, R_064, R_065, R_0...
12 ankieta_01b kod_miasta 46 [M_048, M_014, M_062, M_053, M_041, M_059, M_0...
13 ankieta_01b wiek|liczba osób w rodzinie 58 [58|2, 46|4, 16|4, 28|2, 55|5, 37|3, 35|1, 43|...
14 ankieta_01b m_wykształcenie 1 [nan]
15 ankieta_01b k_wykształcenie 5 [średnie, ś, wyższe, w, wyższe,]
16 ankieta_01b PREFEROWANY TYP SKLEPU 7 [SUPERMARKET, SUPER, OSIEDLOWY, BAZAR, BAZAREK...
17 ankieta_01b preferowana marka sklepu 11 [LIDL, LEWIATAN, ŻABKA, POLOMARKET, ALDI, BIED...
18 ankieta_01b preferowanay towar 7 [Mięso i wędliny, Produkty zbożowe, Produkty m...
19 ankieta_01b preferowany rodzaj promocji 6 [gazetka, karta, reklama, nie, sugestia, nan]
20 ankieta_01b preferowany rodzaj promocji.1 4 [nan, RTV, korzystam, kasjera]
21 ankieta_01b czynnik zakupowy 9 [cena, jakość, marka, dostępność, lokalność, o...
22 ankieta_02 id. 125 [R_001, R_002, R_003, R_004, R_005, R_006, R_0...
23 ankieta_02 miara 2 [dochody roczne, wydatki_na_żywność_mc]
24 ankieta_02 wartość 129 [54, 750, 26, 2300, 50, 430, 45, 507, 28, 0, -...
25 miasta kod_miasta 90 [M_001, M_002, M_003, M_004, M_005, M_006, M_0...
26 miasta Miasto 89 [Warszawa, Kraków, Łódź, Wrocław, Poznań, Gdań...
27 miasta Liczba ludności 90 [1790658, 779115, 684113, 641607, 538633, 4709...
Nazwa Liczba kolumn Liczba wierszy
0 ankieta_01a 11 62
1 ankieta_01b 11 66
2 ankieta_02 3 250
3 miasta 3 90

WYŚWIETLENIE ZAWARTOŚCI TABEL - 5 pierwszych wierszy

Tabela: ankieta_01a

id. kod_miasta wiek|liczba osób w rodzinie m_wykształcenie k_wykształcenie PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar preferowany rodzaj promocji preferowany rodzaj promocji.1 czynnik zakupowy
0 R_001 M_058 30|2 podstawowe NaN BAZAREK POLOMARKET Napoje sugestia kasjera cena
1 R_002 M_050 16|1 podstawowe NaN BAZAREK NETTO Owoce i warzywa gazetka NaN dostępność
2 R_003 M_069 49|1 podstawowe. NaN BAZAREK BIEDRONKA Produkty piekarnicze reklama RTV jakość
3 R_004 M_059 67 lat|1 podstawowe NaN OSIEDLOWY LIDL Produkty piekarnicze gazetka NaN jakość
4 R_005 M_057 38|5 zawodowe NaN OSIEDLOWY LIDL Mięso i wędliny gazetka NaN jakość

Tabela: ankieta_01b

id. kod_miasta wiek|liczba osób w rodzinie m_wykształcenie k_wykształcenie PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar preferowany rodzaj promocji preferowany rodzaj promocji.1 czynnik zakupowy
0 R_060 M_048 58|2 NaN średnie SUPERMARKET LIDL Mięso i wędliny gazetka NaN cena
1 R_061 M_014 46|4 NaN ś SUPERMARKET LEWIATAN Produkty zbożowe gazetka NaN jakość
2 R_062 M_062 16|4 NaN średnie SUPERMARKET ŻABKA Produkty zbożowe karta NaN jakość
3 R_063 M_053 28|2 NaN średnie SUPER ŻABKA Produkty mleczne reklama RTV jakość
4 R_064 M_041 55|5 NaN średnie SUPERMARKET POLOMARKET Produkty mleczne gazetka NaN cena

Tabela: ankieta_02

id. miara wartość
0 R_001 dochody roczne 54
1 R_001 wydatki_na_żywność_mc 750
2 R_002 dochody roczne 26
3 R_002 wydatki_na_żywność_mc 2300
4 R_003 dochody roczne 50

Tabela: miasta

kod_miasta Miasto Liczba ludności
0 M_001 Warszawa 1790658
1 M_002 Kraków 779115
2 M_003 Łódź 684113
3 M_004 Wrocław 641607
4 M_005 Poznań 538633

2. Uporządkowanie danych

2.1.Określenie zasad uporządkowanego zbioru danych

Czym są dane uprządkowane?
  • Koncepcja danych uporządkowanych (tidy data) została opisana przez Hadleya Wickhama i stanowi zbiór zasad, które mają na celu ujednolicenie struktury danych, aby ułatwić ich analizę, manipulację i wizualizację.
  • Kluczowym założeniem tidy data jest to, że dane powinny być przedstawione w sposób „czysty”, co eliminuje niejednoznaczności i pozwala stosować zunifikowane podejście do operacji na danych.
  • Zasady tidy data służą do tworzenia struktury danych, która maksymalizuje efektywność procesów analitycznych, pozwala na automatyzację rutynowych zadań oraz minimalizuje ryzyko popełnienia błędów przy dalszej obróbce i modelowaniu danych.
Zasady danych uporządkowanych

📌 Podstawowe zasady:

  1. Każda zmienna tworzy kolumnę.
  2. Każda obserwacja tworzy wiersz.
  3. Każdy rodzaj jednostki tworzy tabelę.

🎯 Korzyści uporządkowania danych:

  • Ułatwia przekształcanie, analizę i wizualizację.
  • Zapewnia ustrukturyzowany i ustandaryzowany sposób organizacji danych.
  • Łączy fizyczny układ danych z ich semantyką.

🛠 Struktura ramki danych:

  • Kolumny reprezentują nazwy zmiennych (etykiety).
  • Wiersze zawierają wartości obserwacji.

🔍 Semantyka danych:

  • Zbiór danych to zbiór wartości:
    • Liczby → dla zmiennych ilościowych.
    • Ciągi znaków → dla zmiennych jakościowych.
  • Każda wartość należy do konkretnej:
    • Zmiennej (kolumna).
    • Obserwacji (wiersz).

2.2. Identyfikacja czy posiadane dane posiadają oznaki danych nieuporządkowych

W zebranych danych zidentyfikowano oznaki danych nieuporządkowanych:

  1. Podzielone obserwacje tych samych zmiennych pomiędzy wiele tabel
    • ankieta_01a
    • ankieta_01b
  2. Przechowywanie w rożnych wierszach jednej kolumny wartosci wielu zmiennych
    • ankieta_02
  3. Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych
    • ankieta_01a
    • ankieta_01b
  4. Podzielone wartości jednej zmiennej pomiędzy kilka kolumn
    • ankieta_01a
    • ankieta_01b
  5. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych.
  6. Zebrane dane podzielonone pomiędzy wiele plików
    • ankieta_01a
    • ankieta_01b
    • ankieta_02
    • miasta
  7. Zebrane dane w plikach zostały zapisane w różnych formatach
    • *.xlsx
    • *.csv
    • *.json

Powyższe niezgodności i nieprawidłowości w danych należy przekształcić, połączyć. W rezultacie powinnismy otrzymać jeden plik danych zawiejający dane,które będą w następnym etapie stnowić bazę do procesu czyszczenia danych

2.3. Proces porządkowania danych

Code


display(Markdown(f"#### Niezgodnośc nr 1. Dane z ankiety [ankieta_01] zostały pozielona na dwa pliki: ankieta_01a i ankieta_01b"))
display(Markdown(f"* Rozwiązaniem jest połączenie tych plików w jeden pod nazwą (ankieta_01)"))
display(Markdown(f"* Przed połączeniem należy sprawdzić zgodnośc tabel w zakresie kolumn"))

if list(ankieta_01a.columns) == list(ankieta_01b.columns):
    display(Markdown(f"* Dane zgodne"))
    display(Markdown(f"Liczba wierszy w ankieta_01a: {len(ankieta_01a)}"))
    display(Markdown(f"Liczba wierszy w ankieta_01b: {len(ankieta_01b)}"))
else:
    display(Markdown(f"*Dane niezgodne"))

ankieta_01 = pd.concat([ankieta_01a, ankieta_01b], axis=0)
display(Markdown(f"dane z dwóch plków połączno w jeden  ankieta_01: "))
display(Markdown(f"Suma wierszy w ankieta_01: {ankieta_01.shape[0]}"))
#display(ankieta_01)


#---------------------------------------------------------------------------------------------------

print()
display(Markdown(f"#### Niezgodność nr 2. W jednej kolumnie [miara] przechowywane są nazwy 2 różnych zmiennych a w kolumnie [wartość]  wartosci odpowiadające różnym zmiennych w pliku ankieta_02"))
display(ankieta_02.head())
display(Markdown(f"* Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. unpivot - unstack"))
ankieta_02 = ankieta_02.set_index(['id.', 'miara']).unstack().reset_index()
display(Markdown(f"* Przekształcona tabela ankieta_02"))
display(ankieta_02.head(4))
print()








display(Markdown(f"#### Niezgodnośc nr 3. Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych - Zmienna wykształcenie i płeć zostały połączone w jednej kolumnie"))
display(Markdown(f"* Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. pivot - stack"))
display(Markdown(f"* oraz zamiany nazw kolumn"))
display(ankieta_01[['id.',  'm_wykształcenie',  'k_wykształcenie']].sample(5))


t1=ankieta_01[['id.','m_wykształcenie','k_wykształcenie']]
t1=t1.set_index('id.').stack().reset_index()

t1.columns  = ['id.', 'typ_wykształcenia', 'wykształcenie']
t1['typ_wykształcenia'] = t1['typ_wykształcenia'].str.replace('_wykształcenie', '')

t1 = t1.rename(columns={'typ_wykształcenia': 'płeć'})

ankieta_01 = pd.merge(ankieta_01, t1, on = 'id.',how = 'left'   )
ankieta_01 = ankieta_01.drop(['m_wykształcenie','k_wykształcenie'], axis=1)

display(Markdown(f"* Przekształcone kolumny m_wykształcenie','k_wykształcenie' tabela ankieta_02"))
display(ankieta_01[['id.','płeć', 'wykształcenie']].sample(5))
print()






#---------------------------------------------------------------------------------------------------
#Podzielone wartości jednej zmiennej pomiędzy kilka kolumn
#Zmienna preferowany rodzaj promocji została podzielona na dwie kolumny

display(Markdown(f"#### Niezgodnośc nr 4. Podzielone wartości jednej zmiennej pomiędzy kilka kolumn: 'preferowany rodzaj promocji','preferowany rodzaj promocji.1"))
display(Markdown(f"* Rozwiązaniem jest dokonanie połączenia (cat) preferowany rodzaj promocji w jedną kolumnę"))
display(ankieta_01[['id.','preferowany rodzaj promocji','preferowany rodzaj promocji.1']].head(3))


ankieta_01['preferowany rodzaj promocji.1']=ankieta_01['preferowany rodzaj promocji.1'].fillna('_')
ankieta_01['rodzaj promocji'] = ankieta_01['preferowany rodzaj promocji'].astype(str).str.cat(ankieta_01['preferowany rodzaj promocji.1'].astype(str), sep=' ')
ankieta_01['rodzaj promocji'] = ankieta_01['rodzaj promocji'].str.replace('_','')    # usunieto zbędne znaki_
ankieta_01= ankieta_01.drop('preferowany rodzaj promocji', axis=1) # usunięcie zbędnej kolumny
ankieta_01= ankieta_01.drop('preferowany rodzaj promocji.1', axis=1) # usunięcie zbędnej kolumny
display(ankieta_01[['id.','rodzaj promocji']].head(3))
print()



#---------------------------------------------------------------------------------------------------

#### 2.2.6.  Niezgodność nr 6. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych.

display(Markdown(f"#### Niezgodnośc nr 5. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych ('wiek|liczba osób w rodzinie') "))
display(Markdown(f"* Rozwiązaniem jest dokonanie podzielenia kolumny (split) ('wiek|liczba osób w rodzinie') na dwie kolumny :(,'wiek', 'liczba osób w rodzinie') "))
display(ankieta_01[['id.','wiek|liczba osób w rodzinie']].head(3))
ankieta_01[['wiek', 'liczba osób w rodzinie']] = ankieta_01['wiek|liczba osób w rodzinie'].str.split('|', expand=True) # podzielenie kolumny
ankieta_01= ankieta_01.drop('wiek|liczba osób w rodzinie', axis=1) # usunięcie zbędnej kolumny
display(ankieta_01[['id.','wiek', 'liczba osób w rodzinie']].head(3))
print()

#---------------------------------------------------------------------------------------------------



#### 2.2.7.  Niezgodność nr 7. Zmienne podzielonone pomiędzy wiele tabel

display(Markdown(f"#### Niezgodnośc nr 6. Zmienne podzielonone pomiędzy wiele tabel w wielu plikach: (ankieta_01, ankieta_02, miasta )"))
display(Markdown(f"* Rozwiązaniem jest dokonanie połączenia (merge) 3 tabel w jedną tabelę (baza)) "))

ankieta_01 = pd.merge(ankieta_01, miasta, on = 'kod_miasta',how = 'left')
#ankieta_01= ankieta_01.drop('kod_miasta', axis=1) 
ankieta_02.columns= ankieta_02.columns.to_flat_index()

ankieta_02.columns = ['id.', 'dochody', 'wydatki']

baza= pd.merge(ankieta_01,ankieta_02, on = 'id.')
display(baza.head())

Niezgodnośc nr 1. Dane z ankiety [ankieta_01] zostały pozielona na dwa pliki: ankieta_01a i ankieta_01b

  • Rozwiązaniem jest połączenie tych plików w jeden pod nazwą (ankieta_01)
  • Przed połączeniem należy sprawdzić zgodnośc tabel w zakresie kolumn
  • Dane zgodne

Liczba wierszy w ankieta_01a: 62

Liczba wierszy w ankieta_01b: 66

dane z dwóch plków połączno w jeden ankieta_01:

Suma wierszy w ankieta_01: 128

Niezgodność nr 2. W jednej kolumnie [miara] przechowywane są nazwy 2 różnych zmiennych a w kolumnie [wartość] wartosci odpowiadające różnym zmiennych w pliku ankieta_02

id. miara wartość
0 R_001 dochody roczne 54
1 R_001 wydatki_na_żywność_mc 750
2 R_002 dochody roczne 26
3 R_002 wydatki_na_żywność_mc 2300
4 R_003 dochody roczne 50
  • Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. unpivot - unstack
  • Przekształcona tabela ankieta_02
id. wartość
miara dochody roczne wydatki_na_żywność_mc
0 R_001 54 750
1 R_002 26 2300
2 R_003 50 430
3 R_004 45 507

Niezgodnośc nr 3. Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych - Zmienna wykształcenie i płeć zostały połączone w jednej kolumnie

  • Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. pivot - stack
  • oraz zamiany nazw kolumn
id. m_wykształcenie k_wykształcenie
18 R_078 NaN wyższe
17 R_018 średnie NaN
14 R_015 średn NaN
27 R_087 NaN wyższe
11 R_071 NaN wyższe
  • Przekształcone kolumny m_wykształcenie’,‘k_wykształcenie’ tabela ankieta_02
id. płeć wykształcenie
105 R_097 k wyższe
5 R_006 m zawodowe
75 R_067 k średnie
114 R_106 k wyższe
125 R_117 k wyższe

Niezgodnośc nr 4. Podzielone wartości jednej zmiennej pomiędzy kilka kolumn: ‘preferowany rodzaj promocji’,’preferowany rodzaj promocji.1

  • Rozwiązaniem jest dokonanie połączenia (cat) preferowany rodzaj promocji w jedną kolumnę
id. preferowany rodzaj promocji preferowany rodzaj promocji.1
0 R_001 sugestia kasjera
1 R_002 gazetka NaN
2 R_003 reklama RTV
id. rodzaj promocji
0 R_001 sugestia kasjera
1 R_002 gazetka
2 R_003 reklama RTV

Niezgodnośc nr 5. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych (‘wiek|liczba osób w rodzinie’)

  • Rozwiązaniem jest dokonanie podzielenia kolumny (split) (‘wiek|liczba osób w rodzinie’) na dwie kolumny :(,‘wiek’, ‘liczba osób w rodzinie’)
id. wiek|liczba osób w rodzinie
0 R_001 30|2
1 R_002 16|1
2 R_003 49|1
id. wiek liczba osób w rodzinie
0 R_001 30 2
1 R_002 16 1
2 R_003 49 1

Niezgodnośc nr 6. Zmienne podzielonone pomiędzy wiele tabel w wielu plikach: (ankieta_01, ankieta_02, miasta )

  • Rozwiązaniem jest dokonanie połączenia (merge) 3 tabel w jedną tabelę (baza))
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
0 R_001 M_058 BAZAREK POLOMARKET Napoje cena m podstawowe sugestia kasjera 30 2 Głogów 68170 54 750
1 R_002 M_050 BAZAREK NETTO Owoce i warzywa dostępność m podstawowe gazetka 16 1 Suwałki 69442 26 2300
2 R_003 M_069 BAZAREK BIEDRONKA Produkty piekarnicze jakość m podstawowe. reklama RTV 49 1 Świdnica 60281 50 430
3 R_004 M_059 OSIEDLOWY LIDL Produkty piekarnicze jakość m podstawowe gazetka 67 lat 1 Stargard 71464 45 507
4 R_005 M_057 OSIEDLOWY LIDL Mięso i wędliny jakość m zawodowe gazetka 38 5 Ostrowiec Świętokrzyski 69715 28 750

3.Czyszczenie danych

3.1 Wprowadzenie

Odpowiedzią na ww. nieprawidłowości będą odpowiednie oczyszczenie danych z błędów. 1. Zdefiniowanie czystych danych: - Dane muszą być dokładne. - Kompletne. - Spójne. - Ważne. - Aktualne. - Bez duplikatów. - Jednolite.

  1. Rozpoznanie „brudnych” danych:

    Możliwe oznaki zanieczyszczonych danych:

    • Różna pisownia wariantów zmiennej tekstowej
    • Zbędne myślniki.
    • Litery drukowane.
    • Wartości niezgodne z rzeczywistością.
    • Zbędne odstępy.
    • Literówki.
    • Dane niezgodne z wiedzą dziedzinową.
    • Dane niezgodne ze zdrowym rozsądkiem.
    • Wartości liczbowe we wartościach zmiennej kategorycznej, gdzie powinna być wartość tekstowa.
    • Wartości tekstowe w zmiennych ilościowych.
    • Duplikaty.
    • Myślniki zamiast wartości.
    • Zera zamiast wartości.
    • Wartości brakujące-puste.
    • Różne jednostki jednej zmiennej.
  2. Zalety czyszczenia danych:

    • Poprawa jakości danych: Usunięcie błędów, brakujących wartości i anomalii, co zapobiega błędnym wnioskom.
    • Zwiększenie wydajności modeli ML: Dobrze przygotowane dane poprawiają skuteczność algorytmów uczenia maszynowego.
    • Redukcja szumu: Minimalizuje wpływ nieistotnych lub błędnych informacji.
    • Lepsza interpretacja wyników: Przejrzyste i dobrze przygotowane dane ułatwiają analizę i wyciąganie wniosków.
    • Efektywność obliczeniowa: Optymalizacja struktury danych zmniejsza czas i zasoby potrzebne do analizy.

Odpowiedzią na ww. nieprawidłowości będzie odpowiednie oczyszczenie danych z błędów.

3.2. Identyfikacja błędnych danych

W zebranych danych zidentyfikowano błędne i nieprawidłowe dane:

  1. Niejednolite nazwy kolumn
  2. Niejednoznaczna kolejnośc kolumn
  3. Błędy w kolumnach tekstowych
    • różna wielkosć liter wartości tekstowych w wybranych kolumnach
    • wartości zawierają znaki nie literowe
    • wartości z poza zakresu wartosci dozwolonych (wartosci referencyjnych)
  4. Błędy w kolumnach liczbowych
    • wartości zawierają znaki nie liczbowe
    • różne typy zmiennych, typy nie zgodne z założeniami
    • wartości z poza zakresu wartosci dozwolonych (wartosci referencyjnych)

Określenie

Code


display(Markdown(f"#### Niezgodnośc nr 1. Różna wielkość liter w nazwach kolumn"))
display(Markdown(f"#### Niezgodnośc nr 2. Nazwy zawiarają znaki nie będące literami (_)"))
display(Markdown(f"#### Niezgodnośc nr 3. Nieadekwatna kolejność kolumn"))
display(Markdown(f"* Rozwiązaniem jest - Ujednolicenie wielkości liter oraz usunięcie znaków nie będących literami i zmiana kolejności kolumn"))
display(Markdown(f"* Nazwy kolumn przed zmianami:"))
display(baza.columns.to_list())

baza.columns = baza.columns.str.lower().str.replace("_"," ")

kolejnosc = ['id.', 'płeć', 'wykształcenie', 'wiek','liczba osób w rodzinie','preferowany typ sklepu', 'preferowana marka sklepu',
 'preferowanay towar', 'czynnik zakupowy','rodzaj promocji', 'miasto','liczba ludności','dochody', 'wydatki']

baza= baza[kolejnosc]
display(Markdown(f"* Nazwy kolumn po zmianach:"))

display(baza.columns.to_list())
print()


display(Markdown(f"#### Niezgodnośc nr 4. Rózna wielkość liter wartości tekstowych w wybranych kolumnach"))
display(Markdown(f"* Rozwiązaniem jest - Ujednolicenie wielkości"))

display(baza.head(3))

zmienna_tekstowa= ['płeć', 'wykształcenie','preferowany typ sklepu','preferowana marka sklepu', 'preferowanay towar', 'czynnik zakupowy',
 'rodzaj promocji']

#Zamiana drukowanych liter na małe

baza[zmienna_tekstowa]=baza[zmienna_tekstowa].applymap(lambda x: x.lower() if isinstance(x, str) else x)

display(Markdown(f"* tabela po zmianach:"))
display(baza.head(3))
print()




display(Markdown(f"#### Niezgodnośc nr 5. Wartości w kolumnach tekstowych zawieraja niezdozwolone znaki nie literowe "))
display(Markdown(f"* Rozwiązaniem jest - Usunięcie zbędnych znaków ('[A-Z]|[0-9]|_|,|W|\.|\?|-'"))

zmienna_liczbowa = [ 'wiek','liczba osób w rodzinie','liczba ludności',
'dochody', 'wydatki', 'od', 'do', 'średnia częstość zakupów w tyg']
znaki = (r'[A-Z]|[0-9]|_|,|W|\.|\?|-')
  
zmienna_tekstowa= ['płeć', 'wykształcenie','preferowany typ sklepu','preferowana marka sklepu', 'preferowanay towar', 'czynnik zakupowy',
 'rodzaj promocji']
                             
display(Markdown(f"* Dane, które zawierają niedozwolone znaki: *"))
                 

for zmienna in zmienna_tekstowa:
    czy_inne_znaki = baza[zmienna].str.contains(znaki, na=False)

    if any(czy_inne_znaki) == True:
        display(baza.loc[czy_inne_znaki, ['id.',zmienna]])



for zmienna in zmienna_tekstowa:
    czy_inne_znaki = baza[zmienna].str.contains(znaki, na=False)
    if any(czy_inne_znaki) == True:
        baza[zmienna] = baza[zmienna].str.replace(znaki,"")


display(Markdown(f"* Dane po poprawkach: *"))

for zmienna in zmienna_tekstowa:
    czy_inne_znaki = baza[zmienna].str.contains(znaki, na=False)

    if any(czy_inne_znaki) == True:
        display(baza.loc[czy_inne_znaki, ['id.',zmienna]])







display(Markdown(f"#### Niezgodnośc nr 6.  kolumny zmiennych tekstowych zawierają niedozwolone wartoci z poza zakresu referencyjnego "))
display(Markdown(f"* Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych, "))
display(Markdown(f"* Sprawdzenie, w których kolumnach występują niezgodności "))
display(Markdown(f"* Dokonanie zmian błednych danych, "))



reference_values = {
        "Zmienna": ["płeć", "wykształcenie", "preferowany typ sklepu", "preferowana marka sklepu", "preferowany towar", "czynnik zakupowy", "rodzaj promocji", "miasto"],
        "Zakres wartości": ["mężczyzna, kobieta", "podstawowe, zawodowe, średnie, wyższe", "bazarek, osiedlowy, supermarket, galeria", "dowolne", "dowolne", "dowolne", "sugestia kasjera, gazetka, reklama RTV, sms, e-mail, karta, aplikacja, nie korzystam", "dowolne"]
    }

reference_df = pd.DataFrame(reference_values)
display(Markdown(f"* Wartości referencyjne dla zmiennych tekstowych:"))
display(reference_df)





płeć_ref = ['m', 'k']
preferowany_typ_sklepu_ref = ['bazarek', 'osiedlowy', 'supermarket', 'galeria']
wykształcenie_ref = ['podstawowe', 'zawodowe', 'średnie', 'wyższe']

niepasujące_dane_kolumna1 = baza.loc[~baza['płeć'].isin(płeć_ref)]
niepasujące_dane_kolumna2 = baza.loc[~baza['preferowany typ sklepu'].isin(preferowany_typ_sklepu_ref)]
niepasujące_dane_kolumna3 = baza.loc[~baza['wykształcenie'].isin(wykształcenie_ref)]

if not niepasujące_dane_kolumna1.empty:
    print("Niepasujące dane w kolumnie 'płeć':")
    display(niepasujące_dane_kolumna1[['płeć']])

if not niepasujące_dane_kolumna2.empty:
    print("Niepasujące dane w kolumnie 'preferowany typ sklepu':")
    display(niepasujące_dane_kolumna2[['preferowany typ sklepu']])

if not niepasujące_dane_kolumna3.empty:
    print("Niepasujące dane w kolumnie 'wykształcenie':")
    display(niepasujące_dane_kolumna3[['wykształcenie']])

print()




dict_wyk={'średn':'średnie', 'ś': 'średnie', 'w':'wyższe'}
baza['wykształcenie'] = baza['wykształcenie'].replace(dict_wyk)

preferowany_typ_sklepu_ref = {'super': 'supermarket', 'bazar':'bazarek'}
baza['preferowany typ sklepu'] = baza['preferowany typ sklepu'].replace(preferowany_typ_sklepu_ref)


baza['preferowana marka sklepu'] = baza['preferowana marka sklepu'].str.title()


custom_order = ['podstawowe', 'zawodowe','średnie', 'wyższe']
cat_dtype = pd.CategoricalDtype(categories=custom_order, ordered=True)


# Zmiana typu kolumny na "categorical" z zdefiniowanym porządkiem
baza['wykształcenie'] = baza['wykształcenie'].astype(cat_dtype)

zmienna_tekstowa= ['płeć','preferowany typ sklepu','preferowana marka sklepu', 'preferowanay towar', 'czynnik zakupowy',
 'rodzaj promocji']

baza[zmienna_tekstowa]=baza[zmienna_tekstowa].astype('category')

display(Markdown(f"* wartości unikatowe zmiennych po zmianie:"))


unique_wykształcenie = baza['wykształcenie'].unique().tolist()
unique_typ_sklepu = baza['preferowany typ sklepu'].unique().tolist()

print("Unikatowe wartości wykształcenia:", unique_wykształcenie)
print("Unikatowe wartości typu sklepu:", unique_typ_sklepu)

print()



#--------------------------------------------------------------------------------------------------------------------------------------

display(Markdown(f"#### Niezgodnośc nr 7.  kolumny zmiennych liczbowych zawierają niedozwolone wartości z poza zakresu referencyjnego "))
display(Markdown(f"#### Niezgodnośc nr 8.  kolumny zmiennych liczbowych zawierają niedozwolone znaki, np. zł, PLN "))
display(Markdown(f"* Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych, "))
display(Markdown(f"* Sprawdzenie, w których kolumnach występują niezgodności "))
display(Markdown(f"* Dokonanie zmian błednych danych lub zamianę na NaN "))

display(Markdown(f"* Wartości referencyjne dla zmiennych liczbowych:"))

dane = {
    "Cecha": [
        "wiek",
        "liczba osób w rodzinie",
        "dochody",
        "wydatki",
        "dodatkowy parametr"
       
    ],
    "Dozwolone wartości": [
        "18 - 105",
        "1 - 10",
        "> 0",
        "> 0",
        "dochody > wydatki"
       
    ]
}

df = pd.DataFrame(dane)

display(df)



display(Markdown(f"* Sprawdzenie czy w zmiennych liczbowych znajduję się znaki nienumeryczne:"))

zmienna_liczbowa = ['wiek', 'liczba osób w rodzinie', 'dochody', 'wydatki']

for kolumna in zmienna_liczbowa:
    czy_inne_znaki = baza[kolumna].astype(str).str.contains('\D')
    print(f'"{kolumna}" zawiera znaki nie liczbowe : --> {any(czy_inne_znaki)}')
    if any(czy_inne_znaki):
        display(baza.loc[czy_inne_znaki, ['id.', kolumna]])



print()


#pOPRAWA 
# # Numer indeksów, dla których chcemy zmienić wartości na NaN
# indexes_to_replace = [10,5,34,35,77,103,1]

# for index in indexes_to_replace:
#     baza.at[index, 'dochód'] = np.nan


def konwertuj_na_int(wartosc):
    return int(wartosc.replace(' zł', '').replace(' PLN', '').replace(' ', ''))

# Konwersja wartości w kolumnie 'dochody' na int
baza['dochody'] = baza['dochody'].apply(konwertuj_na_int)

# Konwersja wartości w kolumnie 'wydatki' na int
baza['wydatki'] = baza['wydatki'].apply(konwertuj_na_int)


baza['dochody']=baza['dochody']*1000
baza['wydatki']=baza['wydatki']*12





# Konwersja zmiennych z 'object' na 'int64'
baza['wiek'] = pd.to_numeric(baza['wiek'], errors='coerce').astype('float')
baza['liczba osób w rodzinie'] = pd.to_numeric(baza['liczba osób w rodzinie'], errors='coerce').astype('float')




display(Markdown(f"* Sprawdzenie czy w zmiennych liczbowych znajduję się wartości z poza zakresu referencyjnego:"))


#---------------------------------


from IPython.display import display, Markdown

def sprawdz_niezgodnosc(df, warunek, komunikat_bledu, kolumny, nazwa_zmiennej):

    if any(warunek):
        display(Markdown(f'**{komunikat_bledu}**'))
        display(df.loc[warunek, kolumny])
    else:
        display(Markdown(f'**Brak niezgodności w zmiennej "{nazwa_zmiennej}".**'))

# --- Sprawdzanie niezgodności w zmiennej 'dochody' ---
# Warunek: wartości mniejsze od 0 lub równe 0
warunek_dochody = (baza['dochody'] < 0) | (baza['dochody'] == 0)
sprawdz_niezgodnosc(
    df=baza,
    warunek=warunek_dochody,
    komunikat_bledu='Zmienna "dochody" zawiera wartości < 0 lub = 0.',
    kolumny=['id.', 'dochody'],
    nazwa_zmiennej='dochody'
)

# --- Sprawdzanie niezgodności w zmiennej 'wydatki' ---
# Warunek: wartości mniejsze od 0
warunek_wydatki = (baza['wydatki'] < 0)
sprawdz_niezgodnosc(
    df=baza,
    warunek=warunek_wydatki,
    komunikat_bledu='Zmienna "wydatki" zawiera wartości < 0.',
    kolumny=['id.', 'wydatki'],
    nazwa_zmiennej='wydatki'
)

# --- Sprawdzanie niezgodności między 'wydatki' a 'dochody' ---
# Warunek: wydatki są większe od dochodów
warunek_wydatki_dochody = baza['wydatki'] > baza['dochody']
sprawdz_niezgodnosc(
    df=baza,
    warunek=warunek_wydatki_dochody,
    komunikat_bledu='Istnieją wydatki większe od dochodów.',
    kolumny=['id.', 'dochody', 'wydatki'],
    nazwa_zmiennej='wydatki i dochody'
)

# --- Sprawdzanie niezgodności w zmiennej 'wiek' ---
# Warunek: wiek poza zakresem 15-105 lat
warunek_wiek = (baza['wiek'] < 15) | (baza['wiek'] > 105)
sprawdz_niezgodnosc(
    df=baza,
    warunek=warunek_wiek,
    komunikat_bledu='Zmienna "wiek" zawiera wartości spoza zakresu 15-105.',
    kolumny=['id.', 'wiek'],
    nazwa_zmiennej='wiek'
)

# --- Sprawdzanie niezgodności w zmiennej 'liczba osób w rodzinie' ---
# Warunek: liczba osób w rodzinie spoza zakresu 1-10
warunek_liczba_osob = (baza['liczba osób w rodzinie'] < 1) | (baza['liczba osób w rodzinie'] > 10)
sprawdz_niezgodnosc(
    df=baza,
    warunek=warunek_liczba_osob,
    komunikat_bledu='Zmienna "liczba osób w rodzinie" zawiera wartości spoza zakresu 1-10.',
    kolumny=['id.', 'liczba osób w rodzinie'],
    nazwa_zmiennej='liczba osób w rodzinie'
)


display(Markdown(f"Z tych danych wynika, że są to błędne wpisy."))

display(Markdown(f"Z uwagi na niewielką ilość pozyskanych danych usuwanie takich danych znacznie zmniejszyło by liczbę dostępych danych, dlatego zostanie dokonana zamiana błędych wartosci na wartości brakujące, następnie w dalszych krokach braki zostaną obsłużone."))
print()
display(Markdown(f"* tabela zmiennych i nr wierszy do dokonania zmian błędnych danych na wartości brakujące NaN"))

cond_dochody = (baza['dochody'] < 0) | (baza['dochody'] == 0)

# b) Zmienna 'wydatki' - wartości mniejsze niż 0
cond_wydatki = (baza['wydatki'] < 0)

# c) Niezgodność między 'wydatki' a 'dochody' - wydatki większe od dochodów
cond_wydatki_dochody = (baza['wydatki'] > baza['dochody'])

# d) Zmienna 'wiek' - wartości spoza zakresu 15-105
cond_wiek = (baza['wiek'] < 15) | (baza['wiek'] > 105)

# e) Zmienna 'liczba osób w rodzinie' - wartości spoza zakresu 1-10
cond_liczba_osob = (baza['liczba osób w rodzinie'] < 1) | (baza['liczba osób w rodzinie'] > 10)

# 2. Pobranie numerów indeksów (wierszy), które spełniają powyższe warunki:
indeksy_dochody = list(baza.index[cond_dochody])
indeksy_wydatki = list(baza.index[cond_wydatki])
indeksy_wydatki_dochody = list(baza.index[cond_wydatki_dochody])
indeksy_wiek = list(baza.index[cond_wiek])
indeksy_liczba_osob = list(baza.index[cond_liczba_osob])

# 3. Tworzenie nowego DataFrame z informacjami o niezgodnościach:
df_nieprawidlowe = pd.DataFrame({
    'zmienna': ['dochody', 'wydatki', 'wydatki > dochody', 'wiek', 'liczba osób w rodzinie'],
    'nr wiersza do zmiany': [indeksy_dochody, indeksy_wydatki, indeksy_wydatki_dochody, indeksy_wiek, indeksy_liczba_osob]
})

# Wyświetlenie utworzonego DataFrame
display(df_nieprawidlowe)



# 2. Zamiana wartości na NaN w odpowiednich kolumnach:

# Dla 'dochody'
baza.loc[cond_dochody, 'dochody'] = np.nan

# Dla 'wydatki'
baza.loc[cond_wydatki, 'wydatki'] = np.nan

# Dla niezgodności: wydatki większe od dochodów – zamieniamy obie wartości na NaN
baza.loc[cond_wydatki_dochody, ['dochody', 'wydatki']] = np.nan

# Dla 'wiek'
baza.loc[cond_wiek, 'wiek'] = np.nan

# Dla 'liczba osób w rodzinie'
baza.loc[cond_liczba_osob, 'liczba osób w rodzinie'] = np.nan



display(Markdown(f"* Ponowne sprawdzenie poprawnosci referencyjnej zmiennych liczbowych:"))

# Sprawdzanie niezgodności w zmiennej 'dochody'
czy_ujemne = (baza['dochody'] < 0)
czy_zero = (baza['dochody'] == 0)
if any(czy_ujemne) or any(czy_zero):
    print(f'Zmienna "dochody" zawiera wartości < 0 lub = 0.')
    display(baza.loc[czy_ujemne | czy_zero, ['id.', 'dochody']])
else:
    print('Brak niezgodności w zmiennej "dochody".')

# Sprawdzanie niezgodności w zmiennej 'wydatki'
czy_ujemne = (baza['wydatki'] < 0)
if any(czy_ujemne):
    print(f'Zmienna "wydatki" zawiera wartości < 0.')
    display(baza.loc[czy_ujemne, ['id.', 'wydatki']])
else:
    print('Brak niezgodności w zmiennej "wydatki".')

# Sprawdzanie niezgodności między 'wydatki' a 'dochody'
czy_wieksze_wydatki = baza['wydatki'] > baza['dochody']
if any(czy_wieksze_wydatki):
    print('Istnieją wydatki większe od dochodów.')
    display(baza.loc[czy_wieksze_wydatki, ['id.', 'dochody', 'wydatki']])
else:
    print('Brak niezgodności między wydatkami a dochodami.')

# Sprawdzanie niezgodności w zmiennej 'wiek'
czy_poza_zakresem = (baza['wiek'] < 15) | (baza['wiek'] > 105)
if any(czy_poza_zakresem):
    print('Zmienna "wiek" zawiera wartości spoza zakresu 15-105.')
    display(baza.loc[czy_poza_zakresem, ['id.', 'wiek']])
else:
    print('Brak niezgodności w zmiennej "wiek".')

# Sprawdzanie niezgodności w zmiennej 'liczba osób w rodzinie'
czy_poza_zakresem = (baza['liczba osób w rodzinie'] < 1) | (baza['liczba osób w rodzinie'] > 10)
if any(czy_poza_zakresem):
    print('Zmienna "liczba osób w rodzinie" zawiera wartości spoza zakresu 1-10.')
    display(baza.loc[czy_poza_zakresem, ['id.', 'liczba osób w rodzinie']])
else:
    print('Brak niezgodności w zmiennej "liczba osób w rodzinie".')

Niezgodnośc nr 1. Różna wielkość liter w nazwach kolumn

Niezgodnośc nr 2. Nazwy zawiarają znaki nie będące literami (_)

Niezgodnośc nr 3. Nieadekwatna kolejność kolumn

  • Rozwiązaniem jest - Ujednolicenie wielkości liter oraz usunięcie znaków nie będących literami i zmiana kolejności kolumn
  • Nazwy kolumn przed zmianami:
['id.',
 'kod_miasta',
 'PREFEROWANY TYP SKLEPU',
 'preferowana marka sklepu',
 'preferowanay towar',
 'czynnik zakupowy',
 'płeć',
 'wykształcenie',
 'rodzaj promocji',
 'wiek',
 'liczba osób w rodzinie',
 'Miasto',
 'Liczba ludności',
 'dochody',
 'wydatki']
  • Nazwy kolumn po zmianach:
['id.',
 'płeć',
 'wykształcenie',
 'wiek',
 'liczba osób w rodzinie',
 'preferowany typ sklepu',
 'preferowana marka sklepu',
 'preferowanay towar',
 'czynnik zakupowy',
 'rodzaj promocji',
 'miasto',
 'liczba ludności',
 'dochody',
 'wydatki']

Niezgodnośc nr 4. Rózna wielkość liter wartości tekstowych w wybranych kolumnach

  • Rozwiązaniem jest - Ujednolicenie wielkości
id. płeć wykształcenie wiek liczba osób w rodzinie preferowany typ sklepu preferowana marka sklepu preferowanay towar czynnik zakupowy rodzaj promocji miasto liczba ludności dochody wydatki
0 R_001 m podstawowe 30 2 BAZAREK POLOMARKET Napoje cena sugestia kasjera Głogów 68170 54 750
1 R_002 m podstawowe 16 1 BAZAREK NETTO Owoce i warzywa dostępność gazetka Suwałki 69442 26 2300
2 R_003 m podstawowe. 49 1 BAZAREK BIEDRONKA Produkty piekarnicze jakość reklama RTV Świdnica 60281 50 430
  • tabela po zmianach:
id. płeć wykształcenie wiek liczba osób w rodzinie preferowany typ sklepu preferowana marka sklepu preferowanay towar czynnik zakupowy rodzaj promocji miasto liczba ludności dochody wydatki
0 R_001 m podstawowe 30 2 bazarek polomarket napoje cena sugestia kasjera Głogów 68170 54 750
1 R_002 m podstawowe 16 1 bazarek netto owoce i warzywa dostępność gazetka Suwałki 69442 26 2300
2 R_003 m podstawowe. 49 1 bazarek biedronka produkty piekarnicze jakość reklama rtv Świdnica 60281 50 430

Niezgodnośc nr 5. Wartości w kolumnach tekstowych zawieraja niezdozwolone znaki nie literowe

  • Rozwiązaniem jest - Usunięcie zbędnych znaków (’[A-Z]|[0-9]|_|,|W|.|?|-’
  • Dane, które zawierają niedozwolone znaki: *
id. wykształcenie
2 R_003 podstawowe.
52 R_053 śred5nie
103 R_095 wyższe,
  • Dane po poprawkach: *
id. wykształcenie
2 R_003 podstawowe.
52 R_053 śred5nie
103 R_095 wyższe,

Niezgodnośc nr 6. kolumny zmiennych tekstowych zawierają niedozwolone wartoci z poza zakresu referencyjnego

  • Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych,
  • Sprawdzenie, w których kolumnach występują niezgodności
  • Dokonanie zmian błednych danych,
  • Wartości referencyjne dla zmiennych tekstowych:
Zmienna Zakres wartości
0 płeć mężczyzna, kobieta
1 wykształcenie podstawowe, zawodowe, średnie, wyższe
2 preferowany typ sklepu bazarek, osiedlowy, supermarket, galeria
3 preferowana marka sklepu dowolne
4 preferowany towar dowolne
5 czynnik zakupowy dowolne
6 rodzaj promocji sugestia kasjera, gazetka, reklama RTV, sms, e...
7 miasto dowolne
Niepasujące dane w kolumnie 'preferowany typ sklepu':
preferowany typ sklepu
71 super
79 super
84 bazar
85 bazar
120 NaN
126 NaN
Niepasujące dane w kolumnie 'wykształcenie':
wykształcenie
2 podstawowe.
14 średn
52 śred5nie
69 ś
92 w
103 wyższe,
  • wartości unikatowe zmiennych po zmianie:
Unikatowe wartości wykształcenia: ['podstawowe', nan, 'zawodowe', 'średnie', 'wyższe']
Unikatowe wartości typu sklepu: ['bazarek', 'osiedlowy', 'supermarket', 'galeria', nan]

Niezgodnośc nr 7. kolumny zmiennych liczbowych zawierają niedozwolone wartości z poza zakresu referencyjnego

Niezgodnośc nr 8. kolumny zmiennych liczbowych zawierają niedozwolone znaki, np. zł, PLN

  • Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych,
  • Sprawdzenie, w których kolumnach występują niezgodności
  • Dokonanie zmian błednych danych lub zamianę na NaN
  • Wartości referencyjne dla zmiennych liczbowych:
Cecha Dozwolone wartości
0 wiek 18 - 105
1 liczba osób w rodzinie 1 - 10
2 dochody > 0
3 wydatki > 0
4 dodatkowy parametr dochody > wydatki
  • Sprawdzenie czy w zmiennych liczbowych znajduję się znaki nienumeryczne:
"wiek" zawiera znaki nie liczbowe : --> True
id. wiek
3 R_004 67 lat
11 R_012 33 lata
"liczba osób w rodzinie" zawiera znaki nie liczbowe : --> False
"dochody" zawiera znaki nie liczbowe : --> True
id. dochody
6 R_007 29 zł
10 R_011 -31
"wydatki" zawiera znaki nie liczbowe : --> True
id. wydatki
5 R_006 -556
20 R_021 669 PLN
  • Sprawdzenie czy w zmiennych liczbowych znajduję się wartości z poza zakresu referencyjnego:

Zmienna “dochody” zawiera wartości < 0 lub = 0.

id. dochody
5 R_006 0
10 R_011 -31000
86 R_078 0
112 R_104 0

Zmienna “wydatki” zawiera wartości < 0.

id. wydatki
5 R_006 -6672

Istnieją wydatki większe od dochodów.

id. dochody wydatki
1 R_002 26000 27600
10 R_011 -31000 7092
86 R_078 0 10692
112 R_104 0 12024

Zmienna “wiek” zawiera wartości spoza zakresu 15-105.

id. wiek
6 R_007 158.0

Zmienna “liczba osób w rodzinie” zawiera wartości spoza zakresu 1-10.

id. liczba osób w rodzinie
39 R_040 30.0

Z tych danych wynika, że są to błędne wpisy.

Z uwagi na niewielką ilość pozyskanych danych usuwanie takich danych znacznie zmniejszyło by liczbę dostępych danych, dlatego zostanie dokonana zamiana błędych wartosci na wartości brakujące, następnie w dalszych krokach braki zostaną obsłużone.

  • tabela zmiennych i nr wierszy do dokonania zmian błędnych danych na wartości brakujące NaN
zmienna nr wiersza do zmiany
0 dochody [5, 10, 86, 112]
1 wydatki [5]
2 wydatki > dochody [1, 10, 86, 112]
3 wiek [6]
4 liczba osób w rodzinie [39]
  • Ponowne sprawdzenie poprawnosci referencyjnej zmiennych liczbowych:
Brak niezgodności w zmiennej "dochody".
Brak niezgodności w zmiennej "wydatki".
Brak niezgodności między wydatkami a dochodami.
Brak niezgodności w zmiennej "wiek".
Brak niezgodności w zmiennej "liczba osób w rodzinie".
Code

#---------------------------------------------------------------------------------------------------

display(Markdown(f"#### Niezgodnośc nr 9. Duplikaty - Sprawdzenie czy w danych występują duplikaty"))
display(Markdown(f"* Rozwiązaniem jest dokonanie zidentyfokowanie dublujących się danych , a następnie ich usunięcie "))
display(Markdown(f"* Idntyfikacja duplikatów:"))
display(Markdown(f"Rozmiar tabeli z duplikatami: {baza.shape}"))
duplikaty_df = pd.DataFrame({
    'Nazwa pliku': ['baza'],
    'Duplikaty': [baza.duplicated().any()]
})
display(duplikaty_df)
display(Markdown(f"* Wyświetlenie zduplikowanych wierszy:"))
display(baza[baza.duplicated()])
print(f'UWAGA: Usunięto wiersze ({sum(ankieta_01a.duplicated())}) z duplikującymi się danymi')
baza.drop_duplicates(inplace=True)

display(Markdown(f"Rozmiar tabeli po usunięciu duplikatów: {baza.shape}"))

print()

Niezgodnośc nr 9. Duplikaty - Sprawdzenie czy w danych występują duplikaty

  • Rozwiązaniem jest dokonanie zidentyfokowanie dublujących się danych , a następnie ich usunięcie
  • Idntyfikacja duplikatów:

Rozmiar tabeli z duplikatami: (134, 14)

Nazwa pliku Duplikaty
0 baza True
  • Wyświetlenie zduplikowanych wierszy:
id. płeć wykształcenie wiek liczba osób w rodzinie preferowany typ sklepu preferowana marka sklepu preferowanay towar czynnik zakupowy rodzaj promocji miasto liczba ludności dochody wydatki
57 R_057 k średnie 69.0 2.0 osiedlowy Lidl produkty piekarnicze cena gazetka Ostrów Wielkopolski 67825 41000.0 9948.0
58 R_057 k średnie 69.0 2.0 osiedlowy Lidl produkty piekarnicze cena gazetka Ostrów Wielkopolski 67825 41000.0 9948.0
59 R_057 k średnie 69.0 2.0 osiedlowy Lidl produkty piekarnicze cena gazetka Ostrów Wielkopolski 67825 41000.0 9948.0
61 R_058 k średnie 47.0 4.0 supermarket Żabka produkty mleczne marka gazetka Inowrocław 75535 41000.0 9948.0
62 R_058 k średnie 47.0 4.0 supermarket Żabka produkty mleczne marka gazetka Inowrocław 75535 41000.0 9948.0
63 R_058 k średnie 47.0 4.0 supermarket Żabka produkty mleczne marka gazetka Inowrocław 75535 41000.0 9948.0
65 R_059 k średnie 26.0 5.0 supermarket Aldi mięso i wędliny cena sugestia kasjera Toruń 202386 41000.0 9948.0
66 R_059 k średnie 26.0 5.0 supermarket Aldi mięso i wędliny cena sugestia kasjera Toruń 202386 41000.0 9948.0
67 R_059 k średnie 26.0 5.0 supermarket Aldi mięso i wędliny cena sugestia kasjera Toruń 202386 41000.0 9948.0
UWAGA: Usunięto wiersze (3) z duplikującymi się danymi

Rozmiar tabeli po usunięciu duplikatów: (125, 14)

3.4. Obserwacje nietypowe i odstające

3.4.1. Sprawdzenie czy w zmiennych liczbowych wystepują obserwacje odstające ( naturalne, błędy)

Code
# def outliers(df, var):
#     data = df[var]
 
#     def detect_outliers_iqr(data):
#         q1, q3 = np.percentile(data, [25, 75])
#         iqr = q3 - q1
#         lower_bound = q1 - (1.5 * iqr)
#         upper_bound = q3 + (1.5 * iqr)
#         outliers_IQR = [i for i, x in enumerate(data) if x < lower_bound or x > upper_bound]
#         return outliers_IQR

#     def detect_outliers_zscore(data):
#         threshold = 2
#         z_scores = zscore(data)
#         outliers_zscore = [i for i, z in enumerate(z_scores) if abs(z) > threshold]
#         return outliers_zscore


#     # Reshape the data to a 2D array
#     data = np.array(data).reshape(-1, 1)

#     outliers_IQR = detect_outliers_iqr(data)
#     outliers_zscore = detect_outliers_zscore(data)

#     df = pd.DataFrame(data)
#     df['Odstające_IQR'] = np.where(df.index.isin(outliers_IQR), -1, 1)
#     #df['Odstające_mean_std'] = np.where(df.index.isin(outliers_mean_std), -1, 1)
#     df['Odstające_Zscore'] = np.where(df.index.isin(outliers_zscore), -1, 1)

   
#     # Dodatkowa kolumna: Czy_Odstające
#     df['Czy_Odstające'] = np.where((df['Odstające_IQR'] == -1) |
                                
#                                 (df['Odstające_Zscore'] == -1),
#                                   True, False)
#     df = df.loc[df['Czy_Odstające'] == True]
#     df = df.rename(columns={0: var})  # Zmiana nazwy kolumny 0 na Nazwa_Zmiennej
    
#     # Dodaj kolumnę "ile_ident"
#     df['ile_ident'] = df[['Odstające_IQR','Odstające_Zscore']].apply(lambda row: row.value_counts().get(-1, 0), axis=1)
    
#     return df








import numpy as np
import pandas as pd
from scipy.stats import zscore

def detect_outliers_iqr(data: np.ndarray) -> list:
    """
    Wykrywa odchylenia metodą IQR.
    
    Parametry:
        data (np.ndarray): Jednowymiarowa tablica z danymi.
        
    Zwraca:
        list: Lista indeksów, dla których wartość wychodzi poza granice IQR.
    """
    q1, q3 = np.percentile(data, [25, 75])
    iqr = q3 - q1
    lower_bound = q1 - (1.5 * iqr)
    upper_bound = q3 + (1.5 * iqr)
    return [i for i, x in enumerate(data) if x < lower_bound or x > upper_bound]

def detect_outliers_zscore(data: np.ndarray, threshold: float = 2) -> list:
    """
    Wykrywa odchylenia metodą Z-score.
    
    Parametry:
        data (np.ndarray): Jednowymiarowa tablica z danymi.
        threshold (float): Próg wykrywania odchyleń.
        
    Zwraca:
        list: Lista indeksów, dla których wartość z-score przekracza próg.
    """
    z_scores = zscore(data)
    return [i for i, z in enumerate(z_scores) if np.abs(z) > threshold]

def outliers(df: pd.DataFrame, var: str) -> pd.DataFrame:
    """
    Wykrywa odchylenia w zmiennej 'var' przy użyciu metod IQR oraz Z-score.
    
    Dla każdej obserwacji ustawia:
      - Kolumnę 'Odstające_IQR': -1, gdy wartość jest odchyleniem według IQR, inaczej 1.
      - Kolumnę 'Odstające_Zscore': -1, gdy wartość jest odchyleniem według Z-score, inaczej 1.
      - Kolumnę 'Czy_Odstające': True, gdy którakolwiek z metod wykryje odchylenie.
      - Kolumnę 'ile_ident': liczbę metod, które oznaczyły obserwację jako odstającą.
      
    Efekt końcowy – wyświetlenie w konsoli tabeli w formacie markdown – zostaje osiągnięty poprzez
    użycie metody `to_markdown()`.
    
    Parametry:
        df (pd.DataFrame): Ramka danych zawierająca analizowaną zmienną.
        var (str): Nazwa kolumny, dla której mają być wykrywane odchylenia.
        
    Zwraca:
        pd.DataFrame: Tabela zawierająca tylko te obserwacje, które zostały zakwalifikowane jako odstające.
    """
    # Pobierz dane jako jednowymiarowa tablica
    values = np.array(df[var])
    
    # Wykrywanie odchyleń
    indices_iqr = detect_outliers_iqr(values)
    indices_zscore = detect_outliers_zscore(values)
    
    # Utworzenie ramki danych z oryginalnymi wartościami
    out_df = pd.DataFrame({var: values})
    out_df['Odstające_IQR'] = np.where(out_df.index.isin(indices_iqr), -1, 1)
    out_df['Odstające_Zscore'] = np.where(out_df.index.isin(indices_zscore), -1, 1)
    out_df['Czy_Odstające'] = (out_df['Odstające_IQR'] == -1) | (out_df['Odstające_Zscore'] == -1)
    
    # Wybierz tylko te obserwacje, które są odstające
    out_df = out_df[out_df['Czy_Odstające']]
    
    # Dodaj kolumnę z liczbą metod, które wykryły odchylenie
    out_df['ile_ident'] = out_df[['Odstające_IQR', 'Odstające_Zscore']].apply(lambda row: (row == -1).sum(), axis=1)
    
    # Wyświetlenie tabeli w formacie markdown
    display(out_df)
    
    return out_df



display(Markdown(f"* Numer wierszy dla których chcemy zmienić wartości na NaN:"))

zmienna_liczbowa = ['wiek', 'liczba osób w rodzinie',  'dochody', 'wydatki']

for kolumna in zmienna_liczbowa:
    wynik = outliers(baza, kolumna)
    if not wynik.empty:
        print(f' zmienna "{kolumna}" zawiera obserwacje nietypowe :')
        display(Markdown(f"* obserwacje zawiarające wartości odstające liczone wg metod statystycznych IQR oraz Zscore:"))
        display(wynik)
    else:
        print (f' zmienna "{kolumna}" nie zawiera obserwacji nietypowych')


display(Markdown(f"* W wyniku analiy ustalono, że w badanym zbiorze brak jest wartości odstających"))


  • Numer wierszy dla których chcemy zmienić wartości na NaN:
wiek Odstające_IQR Odstające_Zscore Czy_Odstające ile_ident
 zmienna "wiek" nie zawiera obserwacji nietypowych
liczba osób w rodzinie Odstające_IQR Odstające_Zscore Czy_Odstające ile_ident
 zmienna "liczba osób w rodzinie" nie zawiera obserwacji nietypowych
dochody Odstające_IQR Odstające_Zscore Czy_Odstające ile_ident
 zmienna "dochody" nie zawiera obserwacji nietypowych
wydatki Odstające_IQR Odstające_Zscore Czy_Odstające ile_ident
 zmienna "wydatki" nie zawiera obserwacji nietypowych
  • W wyniku analiy ustalono, że w badanym zbiorze brak jest wartości odstających

3.3. Brakujące dane

3.3.1 Sprawdzenie czy w danych wystepują braki (Null, None) [razem, w wierszach , w kolummnach]

Code
def braki_sprawdzenie(dane):
    """
    Analizuje brakujące dane w tabeli.

    Parametry:
        dane (DataFrame): Ramka danych do analizy.

    Zwraca:
        None
    """
    liczba = dane.isnull().sum().sum()
    proc = (liczba / (dane.shape[0]*dane.shape[1])*100).round(2)
    if liczba == 0:
        print('Analiza brakujących danych:')
        print('='*45)
        print('W tabeli nie stwierdzono brakujących danych!')
    else:
        print('Analiza brakujących danych:')
        print('='*45)
        print(f'Liczba brakujących danych w tabeli: {liczba}')
        print(f'Procent brakujących danych w tabeli: {proc}%')
        print('='*45)
        rows_with_missing_data = dane[dane.isnull().any(axis=1)]
        brakujace_dane = rows_with_missing_data.isnull().sum(axis=0)
        udzial_brakujacych_danych = ((rows_with_missing_data.isnull().sum(axis=0) / dane.shape[0])*100).round(1)
        wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych})
        print('Brakujące dane w zmiennych (kolumny):')
        display(wyniki)
        rows_with_missing_data = dane[dane.isnull().any(axis=1)]
        brakujace_dane = rows_with_missing_data.isnull().sum(axis=1)
        udzial_brakujacych_danych = (rows_with_missing_data.isnull().sum(axis=1) / dane.shape[1]*100).round(1)
        wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych})
        print('='*45)
        print('Brakujące dane w obserwacjach (wiersze):')
        display(wyniki)
        print('='*45)
        fig, ax = plt.subplots(figsize=(9.5, 4))
        sns.heatmap(dane.isnull(), cmap='coolwarm', ax=ax)
        rows_with_missing_data = dane[dane.isnull().any(axis=1)]
        print('Tabela z brakującymi danymi:')
        display(rows_with_missing_data)
    
braki_sprawdzenie(baza)

        
Analiza brakujących danych:
=============================================
Liczba brakujących danych w tabeli: 21
Procent brakujących danych w tabeli: 1.2%
=============================================
Brakujące dane w zmiennych (kolumny):
liczba proc
id. 0 0.0
płeć 0 0.0
wykształcenie 3 2.4
wiek 3 2.4
liczba osób w rodzinie 1 0.8
preferowany typ sklepu 2 1.6
preferowana marka sklepu 2 1.6
preferowanay towar 0 0.0
czynnik zakupowy 0 0.0
rodzaj promocji 0 0.0
miasto 0 0.0
liczba ludności 0 0.0
dochody 5 4.0
wydatki 5 4.0
=============================================
Brakujące dane w obserwacjach (wiersze):
liczba proc
1 2 14.3
2 1 7.1
3 1 7.1
5 2 14.3
6 1 7.1
10 2 14.3
11 1 7.1
39 1 7.1
52 1 7.1
86 2 14.3
103 1 7.1
112 2 14.3
120 2 14.3
126 2 14.3
=============================================
Tabela z brakującymi danymi:
id. płeć wykształcenie wiek liczba osób w rodzinie preferowany typ sklepu preferowana marka sklepu preferowanay towar czynnik zakupowy rodzaj promocji miasto liczba ludności dochody wydatki
1 R_002 m podstawowe 16.0 1.0 bazarek Netto owoce i warzywa dostępność gazetka Suwałki 69442 NaN NaN
2 R_003 m NaN 49.0 1.0 bazarek Biedronka produkty piekarnicze jakość reklama rtv Świdnica 60281 50000.0 5160.0
3 R_004 m podstawowe NaN 1.0 osiedlowy Lidl produkty piekarnicze jakość gazetka Stargard 71464 45000.0 6084.0
5 R_006 m zawodowe 28.0 1.0 bazarek Dino owoce i warzywa jakość reklama rtv Warszawa 1790658 NaN NaN
6 R_007 m zawodowe NaN 5.0 bazarek Lidl produkty zbożowe marka gazetka Olsztyn 170904 29000.0 6756.0
10 R_011 m zawodowe 24.0 3.0 osiedlowy Kaufland owoce i warzywa skład gazetka Włocławek 115561 NaN NaN
11 R_012 m zawodowe NaN 5.0 bazarek Biedronka napoje marka email Gorzów Wielkopolski 124581 30000.0 9000.0
39 R_040 m wyższe 69.0 NaN osiedlowy Biedronka napoje marka reklama rtv Piotrków Trybunalski 76279 38000.0 9144.0
52 R_053 k NaN 21.0 3.0 osiedlowy Dino słodycze jakość gazetka Jaworzno 94731 41000.0 9780.0
86 R_078 k wyższe 25.0 2.0 supermarket Netto słodycze jakość nie korzystam Radomsko 49898 NaN NaN
103 R_095 k NaN 75.0 4.0 osiedlowy Biedronka mięso i wędliny jakość reklama rtv Słupsk 93460 48000.0 11544.0
112 R_104 k wyższe 77.0 5.0 galeria Dino owoce i warzywa opakowanie reklama rtv Nowy Sącz 84270 NaN NaN
120 R_112 k wyższe 53.0 5.0 NaN NaN słodycze jakość nan Elbląg 119144 54000.0 12936.0
126 R_118 k wyższe 63.0 2.0 NaN NaN produkty zbożowe jakość nan Ełk 60252 57000.0 14964.0

3.3.2. Obsługa brakujących danych (usuwanie [całość, wg progu], imputacja, utworzenie nowej kategorii np. “b.d.”)

W danych znajdują się barkujące dane.Z uwagi na niewielką ilość pozyskanych danych usuwanie takich danych znacznie zmniejszyło by liczbę dostępych danych, dlatego zostanie dokonana imputacja.  Natomiast w przypadku wierszy  i kolumn , w których znajduje się  = >20 % braków o sustaną usuniete, ponieważ tak znaczna ilość braków może nie być losowa, a imputacja mogła by bardzo zniekształcić oryginalny rozkład 
Code

baza['dochody'].fillna(baza['dochody'].mean().round(1), inplace=True)
baza['wydatki'].fillna(baza['wydatki'].mean().round(1), inplace=True)
baza['wiek'].fillna(baza['wiek'].mode()[0], inplace=True)
baza['liczba osób w rodzinie'].fillna(baza['liczba osób w rodzinie'].mode()[0], inplace=True)
baza['preferowany typ sklepu'].fillna(baza['preferowany typ sklepu'].mode()[0], inplace=True)
baza['preferowana marka sklepu'].fillna(baza['preferowana marka sklepu'].mode()[0], inplace=True)
baza['wykształcenie'].fillna(baza['wykształcenie'].mode()[0], inplace=True)
Code
braki_sprawdzenie(baza)
Analiza brakujących danych:
=============================================
W tabeli nie stwierdzono brakujących danych!

II. Inżynieria i selekcja cech

4. Wzbogacanie danych

Code


# Tworzenie kategorii wieku
bins = [14, 23, 40, 65, 105]
labels = ['14-22', '23-39', '40-65', '> 66']
baza['wiek_kat'] = pd.cut(baza['wiek'], bins=bins, labels=labels)

# Tworzenie kategorii dochodów
quantiles = [0, 0.25, 0.5, 0.75, 1]
labels = ['niski', 'średni', 'wysoki', 'bardzo wysoki']
baza['dochody_kat'] = pd.qcut(baza['dochody'], q=quantiles, labels=labels)

# Tworzenie kategorii wydatków
baza['wydatki_kat'] = pd.qcut(baza['wydatki'], q=quantiles, labels=labels)

# Tworzenie kategorii wielkości miasta
bins = [0, 50000, 200000, 500000, 5000000]
labels = ['<50 tys.', '51 tys.-200 tys.', '201 tys.-500 tys.', 'pow 500 tys.']
baza['wielkość miasta'] = pd.cut(baza['liczba ludności'], bins=bins, labels=labels)




#### 3.4.3. Sprawdzenie czy w zmiennych kategorialnych wystepują obserwacje odstające ( naturalne {obserwacje rzadkie, wysoka kardynalnosć}, błędy)





#### 3.4.4. Obsługa obserwacji odstających *(usuwanie, zamiana na NaN, utworzenie nowej kategorii, przypisanie do nowych kategorii zgodnie wiedzą dziedzinową)*

# preferowana marka sklepu  # rzadkie i wysoka kardynalnosć - zredukować liczbę poziomów  utworzyć osobną grupę "inne" z poziomami poniżej 5


dict_preferowana_marka_sklepu ={'Aldi':'Inna_lokalna','Dino':'Inna_lokalna',
'Carrefour':'Inna_lokalna',
'Polomarket':'Inna_lokalna',
'Społem':'Inna_lokalna',
'Intermache':'Inna_lokalna',
'Groszek':'Inna_lokalna',
'Jeżyk':'Inna_lokalna',
'Lewiatan':'Inna_lokalna'}

baza['preferowana marka sklepu'] = baza['preferowana marka sklepu'].replace(dict_preferowana_marka_sklepu)




dict_rodzaj_promocji ={
    'inne':'inne',
    'e-mail ':'inne',
   'sms ':'inne',
    'aplikacja ' :'inne',
    'nan ':'inne',
    'sugestia kasjera':'inne'
}
baza['rodzaj promocji'] = baza['rodzaj promocji'].replace(dict_rodzaj_promocji)



czynnik_dict = {'dostępność':'inne',
 'skład':'inne',
 'lokalność':'inne',
 'opinie':'inne',
 'opakowanie':'inne',
 'preferencje kulinarne':'inne'}

baza['czynnik zakupowy'] = baza['czynnik zakupowy'].replace(czynnik_dict)




zmienna_tekstowa= [
 'preferowany typ sklepu','preferowana marka sklepu', 'preferowanay towar', 'czynnik zakupowy',
 'rodzaj promocji']

for element in zmienna_tekstowa:
    display(baza[element].value_counts(normalize=True)*100)






   
preferowany typ sklepu
supermarket    30.4
osiedlowy      24.0
bazarek        23.2
galeria        22.4
Name: proportion, dtype: float64
preferowana marka sklepu
Biedronka       26.4
Inna_lokalna    24.8
Lidl            16.8
Kaufland        11.2
Netto           11.2
Żabka            9.6
Name: proportion, dtype: float64
preferowanay towar
produkty piekarnicze    18.4
owoce i warzywa         17.6
mięso i wędliny         16.0
produkty mleczne        15.2
napoje                  14.4
słodycze                 9.6
produkty zbożowe         8.8
Name: proportion, dtype: float64
czynnik zakupowy
jakość    37.6
cena      30.4
marka     16.8
inne      15.2
Name: proportion, dtype: float64
rodzaj promocji
gazetka          36.0
reklama rtv      27.2
nie korzystam    15.2
karta            11.2
inne              8.0
email             2.4
Name: proportion, dtype: float64
Code
zmienna= [
 'wiek_kat',    'dochody_kat',  'wydatki_kat',  'wielkość miasta']


for element in zmienna:
    display(baza[element].value_counts(normalize=True).round(2)*100)
wiek_kat
40-65    42.0
23-39    28.0
> 66     21.0
14-22    10.0
Name: proportion, dtype: float64
dochody_kat
niski            28.0
wysoki           25.0
bardzo wysoki    25.0
średni           22.0
Name: proportion, dtype: float64
wydatki_kat
średni           28.0
niski            26.0
bardzo wysoki    25.0
wysoki           22.0
Name: proportion, dtype: float64
wielkość miasta
51 tys.-200 tys.     72.0
201 tys.-500 tys.    19.0
pow 500 tys.          6.0
<50 tys.              3.0
Name: proportion, dtype: float64
Code
#baza.to_excel('baza.xlsx', index=False)